What is the difference between [:] and [::] when python replicates a list

  • 2020-12-18 01:51:49
  • OfStack

preface


new = old[:]

Python veterans know what this code means. It copies the list old to new. It can be confusing for beginners and should be avoided. Unfortunately [:] markup is widely used, probably because Python programmers don't know a better way to copy lists. However, this article introduces the difference between [:] and [::] when copying python lists. Let's start with 1

We can (shallowly) copy the list using [:] :


l = [1, 2, 3]
z1 = l[:]

We can also (shallowly) copy it using [::] :


z2 = [::]

Now z1 == z2 will be True. After reading the answers for Explain Python 's slice notation, I understand how these images work.

But, my question is is there a difference between the two internally? Is it more efficient to replicate than others, or do you do exactly the same thing?

The best answer

There is absolutely no difference between them, at least in Python 3. If you prefer, you can use dis.dis to check each of these bytecode uses:


l = [1, 2, 3, 4]

Bytecode issued for l [:] :


from dis import dis
dis('l[:]')
 1   0 LOAD_NAME    0 (l)
    3 LOAD_CONST    0 (None)
    6 LOAD_CONST    0 (None)
    9 BUILD_SLICE    2
    12 BINARY_SUBSCR
    13 RETURN_VALUE

Bytecode sent for l [::] :


dis('l[::]')
 1   0 LOAD_NAME    0 (l)
    3 LOAD_CONST    0 (None)
    6 LOAD_CONST    0 (None)
    9 BUILD_SLICE    2
    12 BINARY_SUBSCR
    13 RETURN_VALUE

As you can see, they're exactly the same. 1 nothing (two LOAD_CONSTS) is loaded and applied for both the start and stop values of the build slice (BUILD_SLICE). NONE is the default value described in the slice document in Standard Type hierarchy:

[

Special read-only attributes: start is the lower bound; stop is the upper bound; step is the step value; each is None if omitted. These attributes can have any type.

]

With [:], it has fewer keystrokes.

What is actually interesting is that in Python 2.x, the generated byte code is different and may be slightly more efficient because l [:] has fewer commands:


>>> def foo():
...  l[:]
... 
>>> dis(foo)
 2   0 LOAD_GLOBAL    0 (l)
    3 SLICE+0    
    4 POP_TOP    
    5 LOAD_CONST    0 (None)
    8 RETURN_VALUE 

For l [::] :


>>> def foo2():
...  l[::]
... 
>>> dis(foo2)
 2   0 LOAD_GLOBAL    0 (l)
    3 LOAD_CONST    0 (None)
    6 LOAD_CONST    0 (None)
    9 LOAD_CONST    0 (None)
    12 BUILD_SLICE    3
    15 BINARY_SUBSCR  
    16 POP_TOP    
    17 LOAD_CONST    0 (None)
    20 RETURN_VALUE 

Even if I don't schedule these (I won't, the difference should be small) it seems that l [:] might be a little bit better because it requires fewer instructions.

This similarity is certainly not in the list; It applies to all sequences in Python:


# Note: the Bytecode class exists in Py > 3.4
>>> from dis import Bytecode
>>>
>>> Bytecode('(1, 2, 3)[:]').dis() == Bytecode('(1, 2, 3)[::]').dis() 
True
>>> Bytecode('"string"[:]').dis() == Bytecode('"string"[::]').dis() 
True

The same is true for others.

conclusion


Related articles: